La complexité des opérations sur un ABR est $\Theta(p)$ avec $p$ la profondeur du noeud cherché/ajouté/modifié/supprimé.
Pour être efficace, l’arbre doit être suffisamment équilibré pour que sa hauteur soit de l’ordre de $\Theta(\log n)$. Trois approches sont possibles:
Dans ce cours, nous voyons comment équilibrer a posteriori l'arbre vu précédemment
class Noeud:
def __init__(self,val):
self.clef = val
self.gauche = None
self.droite = None
def __str__(self):
return "{}".format(self.clef)
def inserer(R,val):
if R == None: R = Noeud(val)
elif val < R.clef: R.gauche = inserer(R.gauche,val)
elif val > R.clef: R.droite = inserer(R.droite,val)
else: pass
return R
La technique choisie consiste à modifier deux fois la structure de l'arbre
L'algorithme de linéarisation prend l'arbre R
en entrée. En sortie, il
L
n
L'arbre réorganisé L
est topologiquement équivalent à une liste simplement chainée. Le plus simple est donc d'y insérer les éléments en tête, et donc du plus grand au plus petit.
Il faut donc effectuer un parcours décroissant de l'arbre R
: R.droite
, puis R
, puis R.gauche
# Entrées: arbre de racine R
# liste L en cours de création,
# nombre n d'éléments dans L
# Sorties: L et n mis à jour par l'ajout de R et de ses descendants
def lineariser(R, L = None, n = 0):
if R == None: return L, n
L, n = lineariser(R.droite, L, n)
R.droite = L # ajouter R en tête de liste L
L = R
n += 1
L, n = lineariser(R.gauche, L, n)
R.gauche = None
return L, n
Voyons l'effet de cette fonction sur l'arbre suivant
T = [ 5, 3, 2, 8, 6, 4, 1, 7 ]; R = None;
for t in T: R = inserer(R,t)
h.afficher_arbre_binaire(R)
L, n = lineariser(R.droite,None,0)
# L, n = lineariser(R.droite,None,0)
h.afficher_arbre_binaire(L)
h.afficher_arbre_binaire(R)
R.droite = L; L = R; n += 1
# R.droite = L; L = R; n += 1
h.afficher_arbre_binaire(R)
L, n = lineariser(R.gauche, L, n); R.gauche = None
# L, n = lineariser(R.gauche, L, n); R.gauche = None
print("Il y a",n,"éléments")
h.afficher_arbre_binaire(L)
Il y a 8 éléments
L'algorithme d'arborisation prend en entrée
L
dont tous les enfants gauches sont absentsn
d'éléments de L
il le réorganise pour que
Comme on ne peut parcourir la liste que dans l'ordre
def arboriser(L,n):
if n == 0: return None, L
RG, L = arboriser(L,(n-1)//2)
R = L
R.gauche = RG
L = L.droite
R.droite , L = arboriser(L,n//2)
return R, L
Visualisons le résultat sur notre arbre précédemment linéarisé
h.afficher_arbre_binaire(L)
RG , L = arboriser(L,(n-1)//2)
print((n-1)//2); h.afficher_arbre_binaire(RG)
3
h.afficher_arbre_binaire(L)
R = L; R.gauche = RG; L = L.droite
# R = L; R.gauche = RG; L = L.droite
h.afficher_arbre_binaire(R)
# L pointe vers l'élément de clef 5
R.droite , L = arboriser(L,n//2)
# R.droite , L = arboriser(L,n//2)
print(n//2)
h.afficher_arbre_binaire(R)
print(L)
4
None
L'opération d'équilibrage consiste simplement à réaliser linéarisation puis arborisation
def equilibrer(R):
L, n = lineariser(R)
A, L = arboriser(L,n)
return A
Visualisons-le sur un arbre semi-aléatoire
R = None
for t in range(15): R = inserer(R,1+(7+t*11)%15)
h.afficher_arbre_binaire(R)
R = equilibrer(R)
h.afficher_arbre_binaire(R)
Il faut équilibrer l'arbre quand sa hauteur devient excessive.
Calculer la hauteur est opération de complexité $\Theta(n)$. Il est exclus de le faire à chaque opération.
Ce n'est pas nécessaire. L'opération d'insertion peut nous retourner la profondeur à laquelle elle a inséré l'élément. Si elle est excessive, il faut équilibrer.
def inserer(R,val):
H = 0
if R == None: R = Noeud(val);
elif val < R.clef: R.gauche, H = inserer(R.gauche,val)
elif val > R.clef: R.droite, H = inserer(R.droite,val)
else: pass
return R, H + 1
Par exemple, si l'on veut limiter la profondeur à 3 fois le logarithme de la taille de l'arbre, on pourrait écrire
import numpy as np
R = None; tailleR = 0
for i in range(0,16):
R, H = inserer(R,i); tailleR+= 1
if H > 3 * np.log(tailleR):
print("equilibrer @ i = ",i)
R = equilibrer(R)
equilibrer @ i = 0 equilibrer @ i = 4 equilibrer @ i = 8 equilibrer @ i = 12
h.afficher_arbre_binaire(R)
En conclusion, linéariser un ABR
Arboriser cet ABR dégénéré
Les techniques que vous étudierez en ASD2 sont plus efficaces
© Olivier Cuisenaire, 2018 |